Customer Segmentation - Marcos Damián Pool Canul¶

In [ ]:
from imblearn.over_sampling import SMOTE
from sklearn.ensemble import RandomForestClassifier, AdaBoostClassifier
from sklearn.model_selection import train_test_split, cross_val_score
from sklearn.metrics import confusion_matrix
from sklearn.metrics import f1_score as f1
from plotly.subplots import make_subplots
from sklearn.preprocessing import LabelEncoder
from sklearn.metrics import silhouette_score
from sklearn.metrics.pairwise import cosine_similarity
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import Pipeline
from sklearn.svm import SVC
from sklearn.decomposition import PCA
from sklearn.cluster import KMeans
import matplotlib.pyplot as plt
import scikitplot as skplt
import numpy as np
import pandas as pd
import seaborn as sns
import plotly.offline as pyo
import plotly.express as ex
import plotly.graph_objs as go
import plotly.figure_factory as ff
import scipy.stats as stats
import warnings
warnings.filterwarnings(action="ignore")
pyo.init_notebook_mode()
sns.set_style('darkgrid')
plt.rc('figure', figsize=(18, 9))

1. Exploración de los datos¶

Analizar los datos y explorar los terminos¶

El conjunto de datos "BankChurners.csv" contiene información sobre clientes de un banco.

  1. ID_Customer: Identificador numérico único para cada cliente.
  2. Customer_Age: Edad del cliente.
  3. Gender: Género del cliente (M/F).
  4. Dependent_count: Número de dependientes.
  5. Income_Category: Categoría de ingresos del cliente.
  6. Months_on_book: Cantidad de meses que el cliente ha estado con el banco.
  7. Total_Relationship_Count: Número total de productos del banco que el cliente utiliza.
  8. Months_Inactive_12_mon: Número de meses en los que el cliente estuvo inactivo en los últimos 12 meses.
  9. Contacts_Count_12_mon: Número de veces que el cliente contactó al banco en los últimos 12 meses.
  10. Credit_Limit: Límite de crédito del cliente.
  11. Total_Revolving_Bal: Saldo rotativo total del cliente.
  12. Avg_Open_To_Buy: Promedio de crédito no utilizado por el cliente.
  13. Total_Trans_Amt: Monto total de las transacciones en el último año.
  14. Total_Trans_Ct: Cantidad total de transacciones en el último año.
  15. Avg_Utilization_Ratio: Ratio promedio de utilización del crédito.
In [ ]:
# Cargando los datos del archivo CSV
file_path = '../files/BankChurners.csv'
df = pd.read_csv(file_path)

# Visualizando las primeras filas de los datos para comprender su estructura
df.head()
Out[ ]:
ID_Customer Customer_Age Gender Dependent_count Income_Category Months_on_book Total_Relationship_Count Months_Inactive_12_mon Contacts_Count_12_mon Credit_Limit Total_Revolving_Bal Avg_Open_To_Buy Total_Trans_Amt Total_Trans_Ct Avg_Utilization_Ratio
0 768805383 45 M 3 $60K - $80K 39 5 1 3 12691.0 777 11914.0 1144 42 0.061
1 818770008 49 F 5 Less than $40K 44 6 1 2 8256.0 864 7392.0 1291 33 0.105
2 713982108 51 M 3 $80K - $120K 36 4 1 0 3418.0 0 3418.0 1887 20 0.000
3 769911858 40 F 4 Less than $40K 34 3 4 1 3313.0 2517 796.0 1171 20 0.760
4 709106358 40 M 3 $60K - $80K 21 5 1 0 4716.0 0 4716.0 816 28 0.000
In [ ]:
fig = go.Figure()
fig.add_trace(go.Histogram(x=df["Customer_Age"],name='Histograma de edad'))
fig.update_xaxes(title_text="Edad del cliente")
fig.update_yaxes(title_text="Número de edad del cliente")
fig.show()

fig = go.Figure()
fig.add_trace(go.Box(x=df["Customer_Age"], name='Boxplot de Edad'))
fig.update_xaxes(title_text="Edad del cliente")
fig.update_yaxes(title_text="Número de clientes")
fig.show()
In [ ]:
ex.pie(df, names='Gender', title='Distribución de género',hole=0.33)
In [ ]:
fig = ex.box(df, x='Gender', title='Distribución de género')
fig.show()
In [ ]:
ex.pie(df, names='Dependent_count', title='Recuento de dependientes',hole=0.33)
In [ ]:
ex.pie(df, names='Income_Category', title='Categoría de ingresos',hole=0.33)
In [ ]:
fig = go.Figure()
fig.add_trace(go.Histogram(x=df["Months_on_book"],
              name='Months on book', nbinsx=10))
fig.update_xaxes(title_text="Months on book (5 month periods)")
fig.update_yaxes(title_text="Number of Months on book")
fig.show()

fig = go.Figure()
fig.add_trace(go.Box(x=df["Months_on_book"], name='Months on book'))
fig.update_xaxes(title_text="Months on book")
fig.update_yaxes(title_text="Number of Months on book")
fig.show()
In [ ]:
ex.pie(df, names='Total_Relationship_Count', title='Recuento total de relaciones',hole=0.33)
In [ ]:
fig = go.Figure()
fig.add_trace(go.Histogram(
    x=df["Months_Inactive_12_mon"], name='Meses Inactivo 12 meses'))
fig.update_xaxes(title_text="Meses Inactivo 12 meses")
fig.update_yaxes(title_text="Meses")
fig.show()

fig = go.Figure()
fig.add_trace(go.Box(x=df["Months_Inactive_12_mon"],
              name='Meses Inactivo 12 meses'))
fig.update_xaxes(title_text="Meses Inactivo 12 meses")
fig.update_yaxes(title_text="Meses")
fig.show()
In [ ]:
fig = go.Figure()
fig.add_trace(go.Histogram(
    x=df["Contacts_Count_12_mon"], name='Contacts_Count_12_mon'))
fig.update_xaxes(title_text="Número de contactos 12 meses")
fig.update_yaxes(title_text="Meses")
fig.show()

fig = go.Figure()
fig.add_trace(go.Box(x=df["Contacts_Count_12_mon"],
              name='Contacts_Count_12_mon'))
fig.update_xaxes(title_text="Número de contactos 12 meses")
fig.update_yaxes(title_text="Meses")
fig.show()
In [ ]:
fig = go.Figure()
fig.add_trace(go.Histogram(x=df["Credit_Limit"],
              name='Credit Limit', nbinsx=20))
fig.update_xaxes(title_text="Límite de crédito (períodos de 2K)")
fig.update_yaxes(title_text="Número de clientes que tienen límite de crédito")
fig.show()

fig = go.Figure()
fig.add_trace(go.Box(x=df["Credit_Limit"], name='Credit Limit'))
fig.update_xaxes(title_text="Límite de crédito (períodos de 2K)")
fig.update_yaxes(title_text="Número de clientes que tienen límite de crédito")
fig.show()
In [ ]:
fig = go.Figure()
fig.add_trace(go.Histogram(
    x=df["Total_Revolving_Bal"], name='Saldo rotatorio total', nbinsx=20))
fig.update_xaxes(title_text="Saldo rotatorio total (2K períodos)")
fig.update_yaxes(title_text="Número de clientes Saldo rotatorio total")
fig.show()

fig = go.Figure()
fig.add_trace(go.Box(x=df["Total_Revolving_Bal"],
              name='Saldo rotatorio total'))
fig.update_xaxes(title_text="Saldo rotatorio total (2K períodos)")
fig.update_yaxes(title_text="Número de clientes Saldo rotatorio total")
fig.show()
In [ ]:
fig = go.Figure()
fig.add_trace(go.Histogram(
    x=df["Avg_Open_To_Buy"], name='Avg Open To Buy', nbinsx=20))
fig.update_xaxes(title_text="Avg Open To Buy (2K periods)")
fig.update_yaxes(title_text="Number of Customer Avg Open To Buy")
fig.show()

fig = go.Figure()
fig.add_trace(go.Box(x=df["Avg_Open_To_Buy"], name='Avg Open To Buy'))
fig.update_xaxes(title_text="Avg Open To Buy (2K periods)")
fig.update_yaxes(title_text="Number of Customer Avg Open To Buy")
fig.show()
In [ ]:
fig = go.Figure()
fig.add_trace(go.Histogram(
    x=df["Total_Trans_Amt"], name='Total Trans Amt', nbinsx=20))
fig.update_xaxes(title_text="Total Trans Amt (1K periods)")
fig.update_yaxes(title_text="Number of Customer Total Trans Amt")
fig.show()

fig = go.Figure()
fig.add_trace(go.Box(x=df["Total_Trans_Amt"], name='Total Trans Amt'))
fig.update_xaxes(title_text="Total Trans Amt (1K periods)")
fig.update_yaxes(title_text="Number of Customer Total Trans Amt")
fig.show()
In [ ]:
fig = go.Figure()
fig.add_trace(go.Histogram(x=df["Total_Trans_Ct"],
              name='Total_Trans_Ct', nbinsx=20))
fig.update_xaxes(title_text="Total Trans Ct (10 periods)")
fig.update_yaxes(title_text="Number of Customer Total Trans Ct")
fig.show()

fig = go.Figure()
fig.add_trace(go.Box(x=df["Total_Trans_Ct"], name='Total_Trans_Ct'))
fig.update_xaxes(title_text="Total Trans Ct (10 periods)")
fig.update_yaxes(title_text="Number of Customer Total Trans Ct")
fig.show()
In [ ]:
fig = go.Figure()
fig.add_trace(go.Histogram(
    x=df["Avg_Utilization_Ratio"], name='Avg Utilization Ratio', nbinsx=10))
fig.update_xaxes(title_text="Avg Utilization Ratio (0.1 periods)")
fig.update_yaxes(title_text="Number of Customer Avg Utilization Ratio")
fig.show()

fig = go.Figure()
fig.add_trace(go.Box(x=df["Avg_Utilization_Ratio"],
              name='Avg Utilization Ratio'))
fig.update_xaxes(title_text="Avg Utilization Ratio (0.1 periods)")
fig.update_yaxes(title_text="Number of Customer Avg Utilization Ratio")
fig.show()
In [ ]:
# Crear una figura
fig = go.Figure()

# Añadir un boxplot para cada columna numérica en df
for column in df.select_dtypes(include=['float', 'int']).columns:
    fig.add_trace(go.Box(y=df[column], name=column))

# Actualizar el layout de la figura, incluyendo la rotación de 90 grados en las etiquetas del eje X
fig.update_layout(xaxis_tickangle=90)

# Mostrar la figura
fig.show()
In [ ]:
Box = df.plot(kind='box', subplots=True, layout=(4, 5), sharex=False, sharey=False, figsize=(15,15))
No description has been provided for this image
In [ ]:
histogram = df.hist(figsize=(15, 15), bins=25)
No description has been provided for this image
In [ ]:
# Correction Matrix Plot
correlations = df.corr()
# plot correlation matrix
fig = plt.figure(figsize=(7, 7))
ax = fig.add_subplot(111)
cax = ax.matshow(correlations, vmin=-1, vmax=1)
fig.colorbar(cax)
plt.show()
No description has been provided for this image
In [ ]:
plt.figure(figsize=(9, 7))
sns.heatmap(df.corr(), cmap='coolwarm')
plt.title('Correlation Matrix')
Out[ ]:
Text(0.5, 1.0, 'Correlation Matrix')
No description has been provided for this image
In [ ]:
df.shape
Out[ ]:
(10127, 15)
In [ ]:
df['ID_Customer'].nunique()
Out[ ]:
10127
In [ ]:
df[df['ID_Customer'].isna()]
Out[ ]:
ID_Customer Customer_Age Gender Dependent_count Income_Category Months_on_book Total_Relationship_Count Months_Inactive_12_mon Contacts_Count_12_mon Credit_Limit Total_Revolving_Bal Avg_Open_To_Buy Total_Trans_Amt Total_Trans_Ct Avg_Utilization_Ratio
In [ ]:
df[df.duplicated(['ID_Customer'])]
Out[ ]:
ID_Customer Customer_Age Gender Dependent_count Income_Category Months_on_book Total_Relationship_Count Months_Inactive_12_mon Contacts_Count_12_mon Credit_Limit Total_Revolving_Bal Avg_Open_To_Buy Total_Trans_Amt Total_Trans_Ct Avg_Utilization_Ratio
In [ ]:
df.columns
Out[ ]:
Index(['ID_Customer', 'Customer_Age', 'Gender', 'Dependent_count',
       'Income_Category', 'Months_on_book', 'Total_Relationship_Count',
       'Months_Inactive_12_mon', 'Contacts_Count_12_mon', 'Credit_Limit',
       'Total_Revolving_Bal', 'Avg_Open_To_Buy', 'Total_Trans_Amt',
       'Total_Trans_Ct', 'Avg_Utilization_Ratio'],
      dtype='object')
In [ ]:
df.dtypes
Out[ ]:
ID_Customer                   int64
Customer_Age                  int64
Gender                       object
Dependent_count               int64
Income_Category              object
Months_on_book                int64
Total_Relationship_Count      int64
Months_Inactive_12_mon        int64
Contacts_Count_12_mon         int64
Credit_Limit                float64
Total_Revolving_Bal           int64
Avg_Open_To_Buy             float64
Total_Trans_Amt               int64
Total_Trans_Ct                int64
Avg_Utilization_Ratio       float64
dtype: object
In [ ]:
df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 10127 entries, 0 to 10126
Data columns (total 15 columns):
 #   Column                    Non-Null Count  Dtype  
---  ------                    --------------  -----  
 0   ID_Customer               10127 non-null  int64  
 1   Customer_Age              10127 non-null  int64  
 2   Gender                    10127 non-null  object 
 3   Dependent_count           10127 non-null  int64  
 4   Income_Category           10127 non-null  object 
 5   Months_on_book            10127 non-null  int64  
 6   Total_Relationship_Count  10127 non-null  int64  
 7   Months_Inactive_12_mon    10127 non-null  int64  
 8   Contacts_Count_12_mon     10127 non-null  int64  
 9   Credit_Limit              10127 non-null  float64
 10  Total_Revolving_Bal       10127 non-null  int64  
 11  Avg_Open_To_Buy           10127 non-null  float64
 12  Total_Trans_Amt           10127 non-null  int64  
 13  Total_Trans_Ct            10127 non-null  int64  
 14  Avg_Utilization_Ratio     10127 non-null  float64
dtypes: float64(3), int64(10), object(2)
memory usage: 1.2+ MB

Visualizamos la información del dataframe y podemos observar tanto el nombre de las columnas, como el su numero total que es de 15 y de igual manera podemos observar que contamos con el numero de filas que es de 10126, otra observación que tenemos es el tipo de datos con el que contamos, tenemos(3 columnas que son de tipos flotante),(10 que son de tipo entero) y (2 que son de tipo categoricas).

In [ ]:
df.isnull().sum()
Out[ ]:
ID_Customer                 0
Customer_Age                0
Gender                      0
Dependent_count             0
Income_Category             0
Months_on_book              0
Total_Relationship_Count    0
Months_Inactive_12_mon      0
Contacts_Count_12_mon       0
Credit_Limit                0
Total_Revolving_Bal         0
Avg_Open_To_Buy             0
Total_Trans_Amt             0
Total_Trans_Ct              0
Avg_Utilization_Ratio       0
dtype: int64
In [ ]:
df.describe()
Out[ ]:
ID_Customer Customer_Age Dependent_count Months_on_book Total_Relationship_Count Months_Inactive_12_mon Contacts_Count_12_mon Credit_Limit Total_Revolving_Bal Avg_Open_To_Buy Total_Trans_Amt Total_Trans_Ct Avg_Utilization_Ratio
count 1.012700e+04 10127.000000 10127.000000 10127.000000 10127.000000 10127.000000 10127.000000 10127.000000 10127.000000 10127.000000 10127.000000 10127.000000 10127.000000
mean 7.391776e+08 46.325960 2.346203 35.928409 3.812580 2.341167 2.455317 8631.953698 1162.814061 7469.139637 4404.086304 64.858695 0.274894
std 3.690378e+07 8.016814 1.298908 7.986416 1.554408 1.010622 1.106225 9088.776650 814.987335 9090.685324 3397.129254 23.472570 0.275691
min 7.080821e+08 26.000000 0.000000 13.000000 1.000000 0.000000 0.000000 1438.300000 0.000000 3.000000 510.000000 10.000000 0.000000
25% 7.130368e+08 41.000000 1.000000 31.000000 3.000000 2.000000 2.000000 2555.000000 359.000000 1324.500000 2155.500000 45.000000 0.023000
50% 7.179264e+08 46.000000 2.000000 36.000000 4.000000 2.000000 2.000000 4549.000000 1276.000000 3474.000000 3899.000000 67.000000 0.176000
75% 7.731435e+08 52.000000 3.000000 40.000000 5.000000 3.000000 3.000000 11067.500000 1784.000000 9859.000000 4741.000000 81.000000 0.503000
max 8.283431e+08 73.000000 5.000000 56.000000 6.000000 6.000000 6.000000 34516.000000 2517.000000 34516.000000 18484.000000 139.000000 0.999000

Como podemos observar contamos con valores muy altos desde nuestros datos minimos como los valores maximos, es una técnica recomendable seria escalar nuestros datos para que mantengas un rango similar entre sus valores(0-1).

In [ ]:
df.describe().T
Out[ ]:
count mean std min 25% 50% 75% max
ID_Customer 10127.0 7.391776e+08 3.690378e+07 708082083.0 7.130368e+08 7.179264e+08 7.731435e+08 8.283431e+08
Customer_Age 10127.0 4.632596e+01 8.016814e+00 26.0 4.100000e+01 4.600000e+01 5.200000e+01 7.300000e+01
Dependent_count 10127.0 2.346203e+00 1.298908e+00 0.0 1.000000e+00 2.000000e+00 3.000000e+00 5.000000e+00
Months_on_book 10127.0 3.592841e+01 7.986416e+00 13.0 3.100000e+01 3.600000e+01 4.000000e+01 5.600000e+01
Total_Relationship_Count 10127.0 3.812580e+00 1.554408e+00 1.0 3.000000e+00 4.000000e+00 5.000000e+00 6.000000e+00
Months_Inactive_12_mon 10127.0 2.341167e+00 1.010622e+00 0.0 2.000000e+00 2.000000e+00 3.000000e+00 6.000000e+00
Contacts_Count_12_mon 10127.0 2.455317e+00 1.106225e+00 0.0 2.000000e+00 2.000000e+00 3.000000e+00 6.000000e+00
Credit_Limit 10127.0 8.631954e+03 9.088777e+03 1438.3 2.555000e+03 4.549000e+03 1.106750e+04 3.451600e+04
Total_Revolving_Bal 10127.0 1.162814e+03 8.149873e+02 0.0 3.590000e+02 1.276000e+03 1.784000e+03 2.517000e+03
Avg_Open_To_Buy 10127.0 7.469140e+03 9.090685e+03 3.0 1.324500e+03 3.474000e+03 9.859000e+03 3.451600e+04
Total_Trans_Amt 10127.0 4.404086e+03 3.397129e+03 510.0 2.155500e+03 3.899000e+03 4.741000e+03 1.848400e+04
Total_Trans_Ct 10127.0 6.485869e+01 2.347257e+01 10.0 4.500000e+01 6.700000e+01 8.100000e+01 1.390000e+02
Avg_Utilization_Ratio 10127.0 2.748936e-01 2.756915e-01 0.0 2.300000e-02 1.760000e-01 5.030000e-01 9.990000e-01
In [ ]:
df.Income_Category[df.Income_Category == "Unknown"] = None

¿Es necesario estandarizar los datos?¶

Respuesta: Sí, es recomendable estandarizar los datos en este dataframe antes de aplicar k-medias, especialmente para las columnas numéricas con diferentes escalas. Además, considera transformar las variables categóricas a un formato numérico adecuado.

Para el algoritmo de k-medias, es importante estandarizar los datos cuando las columnas tienen diferentes escalas o unidades. Por ejemplo, columnas como "Customer_Age", "Credit_Limit", "Total_Trans_Amt", tienen diferentes rangos y unidades, lo que podría sesgar los resultados del algoritmo k-medias si no se estandarizan.

Además, para las columnas categóricas como "Gender" e "Income_Category", podrías considerar aplicar técnicas de codificación (como codificación one-hot) para convertirlas en un formato numérico antes de aplicar k-medias, ya que este algoritmo no maneja directamente datos categóricos.

La estandarización de los datos es necesaria antes de aplicar el algoritmo K-Means, ya que este algoritmo es sensible a la magnitud de los datos. La estandarización asegura que cada característica contribuya equitativamente al resultado del análisis y mejora la convergencia del algoritmo.

2. Preparación de los datos¶

Mantenemos la columna "Income_Category"¶

  1. Lectura del Archivo CSV: Se carga un archivo CSV llamado 'BankChurners.csv' desde una ubicación específica en un DataFrame de Pandas llamado dt.

  2. Transformación de 'Income_Category':

    • Se define una función clean_and_convert_income_category para convertir los valores textuales de la columna 'Income_Category' en valores numéricos. Por ejemplo, 'Less than $40K' se convierte en 39000 y '$60K - $80K' en 60000. Los valores 'Unknown' se convierten en 0.
    • Esta función se aplica a la columna 'Income_Category' del DataFrame.
  3. Transformación de 'Gender':

    • Se define una función convert_gender para convertir los valores textuales de género en valores numéricos, donde 'M' se convierte en 1 y 'F' en 0.
    • Esta función se aplica a la columna 'Gender' del DataFrame.
  4. Visualización de Cambios: Se imprime un resumen de las primeras cinco filas del DataFrame para observar las transformaciones aplicadas.

El objetivo de estas operaciones es preparar los datos para un análisis más sencillo, convirtiendo información textual en numérica y estandarizando los formatos de datos.

La transformación de la columna 'Income_Category' de texto a números en el conjunto de datos tiene varias razones:

  1. Facilitar el Análisis Estadístico: Los modelos estadísticos y algoritmos de machine learning suelen requerir que los datos estén en formato numérico. Convertir categorías de ingresos a números permite que estos modelos procesen y analicen los datos más efectivamente.

  2. Consistencia en la Escala: Al convertir las categorías de ingresos en valores numéricos, se logra una escala consistente para esa variable. Esto es especialmente útil para técnicas analíticas que dependen de la magnitud de los valores, como la regresión lineal o los algoritmos basados en distancia.

  3. Reducción de la Ambigüedad: Las categorías de texto como 'Less than $40K' son menos precisas y pueden ser interpretadas de diversas maneras. Al convertirlas en un valor numérico específico, se reduce la ambigüedad y se estandariza la interpretación de estos datos.

  4. Mejora en la Comparabilidad: Los valores numéricos son más fáciles de comparar y ordenar que los textos. Por ejemplo, es más sencillo comparar ingresos de 30000 y 40000 que comparar categorías como 'Less than $40K' y '$30K - $40K'.

  5. Simplificación de la Visualización: Los datos numéricos se pueden visualizar más fácilmente en gráficos y tablas, lo cual es importante para el análisis exploratorio de datos y la presentación de resultados.

  6. Agrupación y Segmentación: La conversión a valores numéricos facilita la agrupación de clientes en segmentos basados en su ingreso, lo cual puede ser útil para estrategias de marketing, análisis de comportamiento del cliente, entre otros.

In [ ]:
# Leer el archivo CSV
file_path = '../files/BankChurners.csv'
dt = pd.read_csv(file_path)

# Definir la función clean_income_category

def clean_and_convert_income_category(value):
    if 'Less than' in value:
        # Extraer el número, restar 1 y convertir a forma numérica completa
        number = int(value.split('$')[1].split('K')[0]) - 1
        return number * 1000
    elif 'Unknown' in value:
        return 0
    else:
        # Tomar el primer valor del rango y convertir a forma numérica completa
        number = int(value.split(' ')[0].replace('$', '').replace('K', ''))
        return number * 1000

# Definir la función convert_gender

def convert_gender(value):
    if value == 'M':
        return 1
    elif value == 'F':
        return 0
    else:
        return value  # en caso de que haya algún valor inesperado

# Aplicar la función al dtFrame
dt['Income_Category'] = dt['Income_Category'].apply(
    clean_and_convert_income_category)

# Aplicando la función convert_gender a la columna 'Gender'
dt['Gender'] = dt['Gender'].apply(convert_gender)

# Opcional: Mostrar los datos modificados
print(dt.head())
   ID_Customer  Customer_Age  Gender  Dependent_count  Income_Category  \
0    768805383            45       1                3            60000   
1    818770008            49       0                5            39000   
2    713982108            51       1                3            80000   
3    769911858            40       0                4            39000   
4    709106358            40       1                3            60000   

   Months_on_book  Total_Relationship_Count  Months_Inactive_12_mon  \
0              39                         5                       1   
1              44                         6                       1   
2              36                         4                       1   
3              34                         3                       4   
4              21                         5                       1   

   Contacts_Count_12_mon  Credit_Limit  Total_Revolving_Bal  Avg_Open_To_Buy  \
0                      3       12691.0                  777          11914.0   
1                      2        8256.0                  864           7392.0   
2                      0        3418.0                    0           3418.0   
3                      1        3313.0                 2517            796.0   
4                      0        4716.0                    0           4716.0   

   Total_Trans_Amt  Total_Trans_Ct  Avg_Utilization_Ratio  
0             1144              42                  0.061  
1             1291              33                  0.105  
2             1887              20                  0.000  
3             1171              20                  0.760  
4              816              28                  0.000  
In [ ]:
dt.head(10)
Out[ ]:
ID_Customer Customer_Age Gender Dependent_count Income_Category Months_on_book Total_Relationship_Count Months_Inactive_12_mon Contacts_Count_12_mon Credit_Limit Total_Revolving_Bal Avg_Open_To_Buy Total_Trans_Amt Total_Trans_Ct Avg_Utilization_Ratio
0 768805383 45 1 3 60000 39 5 1 3 12691.0 777 11914.0 1144 42 0.061
1 818770008 49 0 5 39000 44 6 1 2 8256.0 864 7392.0 1291 33 0.105
2 713982108 51 1 3 80000 36 4 1 0 3418.0 0 3418.0 1887 20 0.000
3 769911858 40 0 4 39000 34 3 4 1 3313.0 2517 796.0 1171 20 0.760
4 709106358 40 1 3 60000 21 5 1 0 4716.0 0 4716.0 816 28 0.000
5 713061558 44 1 2 40000 36 3 1 2 4010.0 1247 2763.0 1088 24 0.311
6 810347208 51 1 4 120000 46 6 1 3 34516.0 2264 32252.0 1330 31 0.066
7 818906208 32 1 0 60000 27 2 2 2 29081.0 1396 27685.0 1538 36 0.048
8 710930508 37 1 3 60000 36 5 2 0 22352.0 2517 19835.0 1350 24 0.113
9 719661558 48 1 2 80000 36 6 3 3 11656.0 1677 9979.0 1441 32 0.144
In [ ]:
dt.describe()
Out[ ]:
ID_Customer Customer_Age Gender Dependent_count Income_Category Months_on_book Total_Relationship_Count Months_Inactive_12_mon Contacts_Count_12_mon Credit_Limit Total_Revolving_Bal Avg_Open_To_Buy Total_Trans_Amt Total_Trans_Ct Avg_Utilization_Ratio
count 1.012700e+04 10127.000000 10127.000000 10127.000000 10127.000000 10127.000000 10127.000000 10127.000000 10127.000000 10127.000000 10127.000000 10127.000000 10127.000000 10127.000000 10127.000000
mean 7.391776e+08 46.325960 0.470919 2.346203 49831.045719 35.928409 3.812580 2.341167 2.455317 8631.953698 1162.814061 7469.139637 4404.086304 64.858695 0.274894
std 3.690378e+07 8.016814 0.499178 1.298908 28927.760351 7.986416 1.554408 1.010622 1.106225 9088.776650 814.987335 9090.685324 3397.129254 23.472570 0.275691
min 7.080821e+08 26.000000 0.000000 0.000000 0.000000 13.000000 1.000000 0.000000 0.000000 1438.300000 0.000000 3.000000 510.000000 10.000000 0.000000
25% 7.130368e+08 41.000000 0.000000 1.000000 39000.000000 31.000000 3.000000 2.000000 2.000000 2555.000000 359.000000 1324.500000 2155.500000 45.000000 0.023000
50% 7.179264e+08 46.000000 0.000000 2.000000 40000.000000 36.000000 4.000000 2.000000 2.000000 4549.000000 1276.000000 3474.000000 3899.000000 67.000000 0.176000
75% 7.731435e+08 52.000000 1.000000 3.000000 60000.000000 40.000000 5.000000 3.000000 3.000000 11067.500000 1784.000000 9859.000000 4741.000000 81.000000 0.503000
max 8.283431e+08 73.000000 1.000000 5.000000 120000.000000 56.000000 6.000000 6.000000 6.000000 34516.000000 2517.000000 34516.000000 18484.000000 139.000000 0.999000

Manteniendo solo valores numericos¶

In [ ]:
lista_n = ["ID_Customer", "Customer_Age","Gender", "Dependent_count", "Income_Category", "Months_on_book",
           "Total_Relationship_Count", "Months_Inactive_12_mon", "Contacts_Count_12_mon",
           "Credit_Limit", "Total_Revolving_Bal", "Avg_Open_To_Buy", "Total_Trans_Amt", "Total_Trans_Ct",
             "Avg_Utilization_Ratio"]  # Selecionamos las columnas numericas

Creamos un lista en la cual almacenamos los nombres de las columnas que contienen valores numericos, omitiendo las categoricas que en este caso son ("Gender" y "Income_Category")

In [ ]:
dt_n = dt[lista_n]
dt_n.head()
Out[ ]:
ID_Customer Customer_Age Gender Dependent_count Income_Category Months_on_book Total_Relationship_Count Months_Inactive_12_mon Contacts_Count_12_mon Credit_Limit Total_Revolving_Bal Avg_Open_To_Buy Total_Trans_Amt Total_Trans_Ct Avg_Utilization_Ratio
0 768805383 45 1 3 60000 39 5 1 3 12691.0 777 11914.0 1144 42 0.061
1 818770008 49 0 5 39000 44 6 1 2 8256.0 864 7392.0 1291 33 0.105
2 713982108 51 1 3 80000 36 4 1 0 3418.0 0 3418.0 1887 20 0.000
3 769911858 40 0 4 39000 34 3 4 1 3313.0 2517 796.0 1171 20 0.760
4 709106358 40 1 3 60000 21 5 1 0 4716.0 0 4716.0 816 28 0.000

Poniendo de indice la columna "ID_Customer"¶

In [ ]:
# Seleccionamos la columnas que será el indice
df = dt_n.set_index("ID_Customer")
df.head()
Out[ ]:
Customer_Age Gender Dependent_count Income_Category Months_on_book Total_Relationship_Count Months_Inactive_12_mon Contacts_Count_12_mon Credit_Limit Total_Revolving_Bal Avg_Open_To_Buy Total_Trans_Amt Total_Trans_Ct Avg_Utilization_Ratio
ID_Customer
768805383 45 1 3 60000 39 5 1 3 12691.0 777 11914.0 1144 42 0.061
818770008 49 0 5 39000 44 6 1 2 8256.0 864 7392.0 1291 33 0.105
713982108 51 1 3 80000 36 4 1 0 3418.0 0 3418.0 1887 20 0.000
769911858 40 0 4 39000 34 3 4 1 3313.0 2517 796.0 1171 20 0.760
709106358 40 1 3 60000 21 5 1 0 4716.0 0 4716.0 816 28 0.000

Lo siguien que se realizo fue usar la el método "set_index" de pandas para sustutuir en indice anterios por la columna "ID_Customer", hacemos una visualización y verificamos que el indice se haya cambiado de manera correcta.

Tomando en cuenta la exploración de los graficos de caja, podemos observar que la mayoria de las columnas cuentan con valores atípicos.

In [ ]:
plt.figure(figsize=(15, 6))
df.boxplot(rot=(90))
plt.show()
No description has been provided for this image

Al hacer un visualización a los datos podemos observar que con tamos con datos atipicos.

In [ ]:
df.isnull().sum().sum()
Out[ ]:
0

Podemos observa que en el dataframe original no contamos con datos nulos solo atipicos.

Limpiando nuestros datos tomando en cuenta la Desviación estandar de 3 y 5.¶

Desviacion estandar de 3¶

In [ ]:
media = df.mean()  # sacamos la media para cada columna del dataframe
desviacion = df.std()  # sacamos la desviación estandar de cada columna del dataframe

determinamos la media de cada columna y la desviación estandar

In [ ]:
std_3 = 3 * desviacion  # tomamos en cuenta la desviación estandar de 3
# hacemos el calculo para tomar solo la desviación estandar que estan por debajo
df_std_3 = df[(df-media).abs() <= std_3]
df_std_3.head()
Out[ ]:
Customer_Age Gender Dependent_count Income_Category Months_on_book Total_Relationship_Count Months_Inactive_12_mon Contacts_Count_12_mon Credit_Limit Total_Revolving_Bal Avg_Open_To_Buy Total_Trans_Amt Total_Trans_Ct Avg_Utilization_Ratio
ID_Customer
768805383 45.0 1 3 60000 39 5 1.0 3.0 12691.0 777 11914.0 1144.0 42.0 0.061
818770008 49.0 0 5 39000 44 6 1.0 2.0 8256.0 864 7392.0 1291.0 33.0 0.105
713982108 51.0 1 3 80000 36 4 1.0 0.0 3418.0 0 3418.0 1887.0 20.0 0.000
769911858 40.0 0 4 39000 34 3 4.0 1.0 3313.0 2517 796.0 1171.0 20.0 0.760
709106358 40.0 1 3 60000 21 5 1.0 0.0 4716.0 0 4716.0 816.0 28.0 0.000

Ya se ha aplicado la desviación estandar a 3

In [ ]:
plt.figure(figsize=(15, 6))  # indicamos el tamaño que tendrá nuestro gráfico
df_std_3.boxplot(rot=90)  # rotamos a 90°
plt.show()  # visualizamos nuestra gráfica
No description has been provided for this image
In [ ]:
df.dtypes
Out[ ]:
Customer_Age                  int64
Gender                        int64
Dependent_count               int64
Income_Category               int64
Months_on_book                int64
Total_Relationship_Count      int64
Months_Inactive_12_mon        int64
Contacts_Count_12_mon         int64
Credit_Limit                float64
Total_Revolving_Bal           int64
Avg_Open_To_Buy             float64
Total_Trans_Amt               int64
Total_Trans_Ct                int64
Avg_Utilization_Ratio       float64
dtype: object

Visualizamos el gráfico y podemos observar que los datos atipicos han disminuido significativamente.

In [ ]:
df_std_3.isnull().sum().sum()  # verificamos si contamos con datos nulos
Out[ ]:
572

Verificamos si al aplicar la desviación estandar a 3, y como podemos observar ahora contamos con datos nulos que son un total de 572, que es aproximadamente el 6% de los dato, dado a la cantidad de datos podemos obtar por eliminarlos.

Desviación estandar de 5¶

In [ ]:
std_5 = 5 * desviacion
df_std_5 = df[(df-media).abs() <= std_5]
df_std_5.head()
Out[ ]:
Customer_Age Gender Dependent_count Income_Category Months_on_book Total_Relationship_Count Months_Inactive_12_mon Contacts_Count_12_mon Credit_Limit Total_Revolving_Bal Avg_Open_To_Buy Total_Trans_Amt Total_Trans_Ct Avg_Utilization_Ratio
ID_Customer
768805383 45 1 3 60000 39 5 1 3 12691.0 777 11914.0 1144 42 0.061
818770008 49 0 5 39000 44 6 1 2 8256.0 864 7392.0 1291 33 0.105
713982108 51 1 3 80000 36 4 1 0 3418.0 0 3418.0 1887 20 0.000
769911858 40 0 4 39000 34 3 4 1 3313.0 2517 796.0 1171 20 0.760
709106358 40 1 3 60000 21 5 1 0 4716.0 0 4716.0 816 28 0.000
In [ ]:
plt.figure(figsize=(15, 6))
df_std_5.boxplot(rot=(90))
plt.show()
No description has been provided for this image

Al aplicar la desviación estandar a 5, podemos ver que los valores atipicos no han desminuido demasiado, por lo que se llega a la conclusión que es mejor trabajar con 3 desviaciones estandar ya que da mejores resultados al disminuir los datos atipicos.

In [ ]:
df_std_5.isnull().sum().sum()
Out[ ]:
0

¿Hay datos nulos?¶

Si se identifican datos atípicos en el conjunto, pero no se detectan valores nulos.

Optamos por usar una desviación estandar de 3¶

Eliminando los datos nulos generados al utilizar 3 desviaciones estandar¶

In [ ]:
df_std_3_1 = df_std_3.dropna() # eliminamos todos los datos nulos de nuestros dataframe
df_std_3_1.isnull().sum().sum()  # verificamos si contamos con datos nulos
Out[ ]:
0

Eliminamos las filas que continen datos nulos y verificamos que la eliminación se haya realizado de manera correcta, como podemos observar ya no contamos con datos nulos.

In [ ]:
#Identificar si datos atipicos
df_std_3_1.isnull().sum().sort_values()
Out[ ]:
Customer_Age                0
Gender                      0
Dependent_count             0
Income_Category             0
Months_on_book              0
Total_Relationship_Count    0
Months_Inactive_12_mon      0
Contacts_Count_12_mon       0
Credit_Limit                0
Total_Revolving_Bal         0
Avg_Open_To_Buy             0
Total_Trans_Amt             0
Total_Trans_Ct              0
Avg_Utilization_Ratio       0
dtype: int64

Estandarizando (Escalando) nuestros datos.¶

In [ ]:
Scaler = StandardScaler()  # Creamos nuestro objeto escalable
Scaler.fit(df_std_3_1)  # Aplicando la estandarización
dt_estandarizado = Scaler.transform(df_std_3_1)  # Aplicamos las transformación a nuestro dataframe

Escalamos nuestros datos para que tengan magnitudes iguales las cuales se encuentren en un rago de (0 a 1).

3. Segmentación de datos¶

K-means: Técnica del Codo¶

In [ ]:
# Inicializa una lista para almacenar los valores de la inercia (within-cluster sum of squares) para cada k
inertia_values = []

# Define el rango de valores de k que deseas probar
k_range = range(2, 11)  # Prueba desde 2 hasta 10 clusters

# Itera sobre cada valor de k y ajusta el modelo K-Means
for k in k_range:
    kmeans = KMeans(n_clusters=k, random_state=42)
    kmeans.fit(dt_estandarizado)

    # Calcula la inercia y agrega el valor a la lista
    inertia_values.append(kmeans.inertia_)

# Visualiza la técnica del codo para determinar el número óptimo de clusters
plt.plot(k_range, inertia_values, marker='o')
plt.xlabel('Número de clusters (k)')
plt.ylabel('Inercia')
plt.title('Técnica del codo')
plt.show()
No description has been provided for this image

¿Cuántos grupos son los ideales?¶

La justificación para usar 4 grupos en el algoritmo de k-means se basa en la observación de la técnica del codo aplicada durante la segmentación de datos. Según el análisis, la gráfica muestra una inclinación que se hacía menos pronunciada después de 4 clusters. Esto implica que añadir más clusters más allá de 4 no resultaría en una mejora significativa en la varianza explicada por los grupos.

En la técnica del codo, se busca el punto en el que aumentar el número de clusters deja de ofrecer un beneficio significativo en la reducción de la suma de las distancias al cuadrado de cada punto a su centro de cluster más cercano (inercia). Esto se puede visualizar como un cambio en la pendiente de la curva de inercia, donde se vuelve menos empinada, indicando que los clusters adicionales no están capturando tanta 'nueva varianza' en los datos como los clusters anteriores.

4. Perfilado de clientes¶

In [ ]:
# Convertimos nuestros datos escalados en un dataframe
dt_estandarizado = pd.DataFrame(dt_estandarizado, columns=df_std_3_1.columns)
dt_estandarizado.head()
Out[ ]:
Customer_Age Gender Dependent_count Income_Category Months_on_book Total_Relationship_Count Months_Inactive_12_mon Contacts_Count_12_mon Credit_Limit Total_Revolving_Bal Avg_Open_To_Buy Total_Trans_Amt Total_Trans_Ct Avg_Utilization_Ratio
0 -0.170050 1.073173 0.502005 0.356367 0.382027 0.737698 -1.394283 0.517484 0.477434 -0.466137 0.519532 -1.076171 -0.962610 -0.785724
1 0.329850 -0.931817 2.045394 -0.370199 1.010997 1.385956 -1.394283 -0.408601 -0.018040 -0.359519 0.014723 -1.019948 -1.375910 -0.627631
2 0.579801 1.073173 0.502005 1.048335 0.004645 0.089441 -1.394283 -2.260770 -0.558537 -1.418343 -0.428910 -0.791995 -1.972900 -1.004900
3 -0.794925 -0.931817 1.273700 -0.370199 -0.246943 -0.558817 1.830299 -1.334686 -0.570268 1.666218 -0.721615 -1.065844 -1.972900 1.725810
4 -0.794925 1.073173 0.502005 0.356367 -1.882266 0.737698 -1.394283 -2.260770 -0.413526 -1.418343 -0.284009 -1.201621 -1.605522 -1.004900
In [ ]:
kmeans = KMeans(n_clusters=4, random_state=42)
dt_estandarizado['cluster'] = kmeans.fit_predict(dt_estandarizado)

# Realiza la perfilación de clientes para cada grupo
for grupo_num in range(4):
    perfil_grupo = dt_estandarizado[dt_estandarizado['cluster']
                                    == grupo_num].describe()
    print(f"Perfil del cluster {grupo_num}:")
    print(perfil_grupo)
    print()
Perfil del cluster 0:
       Customer_Age      Gender  Dependent_count  Income_Category  \
count    799.000000  799.000000       799.000000       799.000000   
mean      -0.083396    0.275192         0.082837         0.158520   
std        0.904242    0.982027         0.998595         1.000452   
min       -2.419600   -0.931817        -1.813079        -1.719536   
25%       -0.669950   -0.931817        -0.269690        -0.370199   
50%       -0.170050    1.073173        -0.269690        -0.335601   
75%        0.579801    1.073173         0.502005         1.048335   
max        2.079501    1.073173         2.045394         2.432271   

       Months_on_book  Total_Relationship_Count  Months_Inactive_12_mon  \
count      799.000000                799.000000              799.000000   
mean        -0.051089                 -1.293076               -0.157992   
std          0.949089                  0.607122                0.951985   
min         -2.888619                 -1.855332               -1.394283   
25%         -0.498531                 -1.855332               -1.394283   
50%          0.004645                 -1.207075               -0.319423   
75%          0.382027                 -1.207075                0.755438   
max          2.520526                  1.385956                2.905160   

       Contacts_Count_12_mon  Credit_Limit  Total_Revolving_Bal  \
count             799.000000    799.000000           799.000000   
mean               -0.305445      0.619626             0.232077   
std                 0.842904      1.134447             0.920845   
min                -2.260770     -0.737064            -1.418343   
25%                -1.334686     -0.286669            -0.260867   
50%                -0.408601      0.265615             0.378227   
75%                 0.517484      1.311864             0.877001   
max                 2.369653      2.915704             1.666218   

       Avg_Open_To_Buy  Total_Trans_Amt  Total_Trans_Ct  \
count       799.000000       799.000000      799.000000   
mean          0.598013         2.529214        1.549494   
std           1.132895         1.136173        0.682604   
min          -0.748742         0.028022       -0.549309   
25%          -0.314374         1.528264        1.103893   
50%           0.237098         1.904807        1.563116   
75%           1.269657         3.764188        1.976416   
max           3.042685         4.067679        3.170395   

       Avg_Utilization_Ratio  cluster  
count             799.000000    799.0  
mean               -0.408194      0.0  
std                 0.599348      0.0  
min                -1.004900      0.0  
25%                -0.834230      0.0  
50%                -0.598886      0.0  
75%                -0.153349      0.0  
max                 1.876717      0.0  

Perfil del cluster 1:
       Customer_Age       Gender  Dependent_count  Income_Category  \
count   1838.000000  1838.000000      1838.000000      1838.000000   
mean       0.054266     1.009903         0.120356         1.151095   
std        0.875305     0.350597         0.936159         0.986901   
min       -2.544575    -0.931817        -1.813079        -1.719536   
25%       -0.544975     1.073173        -0.269690         0.356367   
50%        0.079900     1.073173         0.502005         1.048335   
75%        0.704776     1.073173         0.502005         2.432271   
max        2.329451     1.073173         2.045394         2.432271   

       Months_on_book  Total_Relationship_Count  Months_Inactive_12_mon  \
count     1838.000000               1838.000000             1838.000000   
mean         0.055428                  0.167387               -0.001292   
std          0.901276                  0.918822                0.970950   
min         -2.888619                 -1.855332               -2.469144   
25%         -0.498531                 -0.558817               -0.319423   
50%          0.004645                  0.089441               -0.319423   
75%          0.633615                  0.737698                0.755438   
max          2.520526                  1.385956                2.905160   

       Contacts_Count_12_mon  Credit_Limit  Total_Revolving_Bal  \
count            1838.000000   1838.000000          1838.000000   
mean                0.117423      1.446687             0.025396   
std                 1.033306      1.036132             0.969071   
min                -2.260770     -0.593617            -1.418343   
25%                -0.408601      0.545331            -0.667730   
50%                 0.517484      1.276170             0.149060   
75%                 0.517484      2.521978             0.725041   
max                 2.369653      2.915704             1.666218   

       Avg_Open_To_Buy  Total_Trans_Amt  Total_Trans_Ct  \
count      1838.000000      1838.000000     1838.000000   
mean          1.443272        -0.324195       -0.327768   
std           1.041659         0.653332        0.895163   
min          -0.588770        -1.311008       -2.432123   
25%           0.552130        -0.864186       -1.100377   
50%           1.289472        -0.412393       -0.227853   
75%           2.531289         0.043226        0.415059   
max           3.042685         2.429556        1.654960   

       Avg_Utilization_Ratio  cluster  
count            1838.000000   1838.0  
mean               -0.751375      1.0  
std                 0.229216      0.0  
min                -1.004900      1.0  
25%                -0.922260      1.0  
50%                -0.789317      1.0  
75%                -0.642003      1.0  
max                 0.292187      1.0  

Perfil del cluster 2:
       Customer_Age       Gender  Dependent_count  Income_Category  \
count   2858.000000  2858.000000      2858.000000      2858.000000   
mean      -0.032875    -0.302539        -0.027219        -0.415644   
std        1.020902     0.930595         1.011842         0.749714   
min       -2.544575    -0.931817        -1.813079        -1.719536   
25%       -0.669950    -0.931817        -1.041384        -0.370199   
50%       -0.045075    -0.931817        -0.269690        -0.370199   
75%        0.579801     1.073173         0.502005        -0.335601   
max        2.704376     1.073173         2.045394         2.432271   

       Months_on_book  Total_Relationship_Count  Months_Inactive_12_mon  \
count     2858.000000               2858.000000             2858.000000   
mean        -0.018287                  0.047025                0.086753   
std          1.004926                  0.956257                1.001412   
min         -2.888619                 -1.855332               -2.469144   
25%         -0.624325                 -0.558817               -0.319423   
50%          0.004645                  0.089441               -0.319423   
75%          0.507821                  0.737698                0.755438   
max          2.520526                  1.385956                2.905160   

       Contacts_Count_12_mon  Credit_Limit  Total_Revolving_Bal  \
count            2858.000000   2858.000000          2858.000000   
mean                0.116980     -0.339665            -0.996148   
std                 0.994613      0.476375             0.616368   
min                -2.260770     -0.779708            -1.418343   
25%                -0.408601     -0.723407            -1.418343   
50%                 0.517484     -0.478323            -1.418343   
75%                 0.517484     -0.112750            -0.501982   
max                 2.369653      2.165845             1.467688   

       Avg_Open_To_Buy  Total_Trans_Amt  Total_Trans_Ct  \
count      2858.000000      2858.000000     2858.000000   
mean         -0.248664        -0.249258       -0.193404   
std           0.454378         0.562795        0.888322   
min          -0.722251        -1.318657       -2.432123   
25%          -0.604092        -0.697907       -0.962610   
50%          -0.392127        -0.245827       -0.044164   
75%          -0.044917         0.179385        0.552825   
max           2.293397         2.422290        1.838649   

       Avg_Utilization_Ratio  cluster  
count            2858.000000   2858.0  
mean               -0.787522      2.0  
std                 0.337074      0.0  
min                -1.004900      2.0  
25%                -1.004900      2.0  
50%                -1.004900      2.0  
75%                -0.584514      2.0  
max                 0.615561      2.0  

Perfil del cluster 3:
       Customer_Age       Gender  Dependent_count  Income_Category  \
count   4065.000000  4065.000000      4065.000000      4065.000000   
mean       0.014969    -0.298013        -0.051565        -0.259399   
std        1.053144     0.932350         1.014681         0.726251   
min       -2.544575    -0.931817        -1.813079        -1.719536   
25%       -0.794925    -0.931817        -1.041384        -0.370199   
50%       -0.045075    -0.931817        -0.269690        -0.370199   
75%        0.829751     1.073173         0.502005        -0.335601   
max        2.954326     1.073173         2.045394         2.432271   

       Months_on_book  Total_Relationship_Count  Months_Inactive_12_mon  \
count     4065.000000               4065.000000             4065.000000   
mean        -0.002163                  0.145416               -0.029356   
std          1.047009                  0.944152                1.015982   
min         -2.888619                 -1.855332               -2.469144   
25%         -0.624325                 -0.558817               -0.319423   
50%          0.004645                  0.089441               -0.319423   
75%          0.633615                  0.737698                0.755438   
max          2.520526                  1.385956                2.905160   

       Contacts_Count_12_mon  Credit_Limit  Total_Revolving_Bal  \
count            4065.000000   4065.000000          4065.000000   
mean               -0.075302     -0.537104             0.643268   
std                 0.997447      0.251263             0.609653   
min                -2.260770     -0.779708            -0.790892   
25%                -0.408601     -0.689025             0.195628   
50%                -0.408601     -0.622664             0.618423   
75%                 0.517484     -0.472849             1.086560   
max                 2.369653      1.412970             1.666218   

       Avg_Open_To_Buy  Total_Trans_Amt  Total_Trans_Ct  \
count      4065.000000      4065.000000     4065.000000   
mean         -0.595293        -0.175299       -0.020384   
std           0.233251         0.551045        0.892506   
min          -0.810140        -1.285765       -2.340278   
25%          -0.733225        -0.702497       -0.778921   
50%          -0.690915        -0.008695        0.185447   
75%          -0.539205         0.229202        0.690592   
max           1.260113         2.423437        1.838649   

       Avg_Utilization_Ratio  cluster  
count            4065.000000   4065.0  
mean                0.973656      3.0  
std                 0.711143      0.0  
min                -0.602479      3.0  
25%                 0.417944      3.0  
50%                 1.017981      3.0  
75%                 1.517413      3.0  
max                 2.584546      3.0  

In [ ]:
dt_estandarizado.head(10)
Out[ ]:
Customer_Age Gender Dependent_count Income_Category Months_on_book Total_Relationship_Count Months_Inactive_12_mon Contacts_Count_12_mon Credit_Limit Total_Revolving_Bal Avg_Open_To_Buy Total_Trans_Amt Total_Trans_Ct Avg_Utilization_Ratio cluster
0 -0.170050 1.073173 0.502005 0.356367 0.382027 0.737698 -1.394283 0.517484 0.477434 -0.466137 0.519532 -1.076171 -0.962610 -0.785724 1
1 0.329850 -0.931817 2.045394 -0.370199 1.010997 1.385956 -1.394283 -0.408601 -0.018040 -0.359519 0.014723 -1.019948 -1.375910 -0.627631 2
2 0.579801 1.073173 0.502005 1.048335 0.004645 0.089441 -1.394283 -2.260770 -0.558537 -1.418343 -0.428910 -0.791995 -1.972900 -1.004900 2
3 -0.794925 -0.931817 1.273700 -0.370199 -0.246943 -0.558817 1.830299 -1.334686 -0.570268 1.666218 -0.721615 -1.065844 -1.972900 1.725810 3
4 -0.794925 1.073173 0.502005 0.356367 -1.882266 0.737698 -1.394283 -2.260770 -0.413526 -1.418343 -0.284009 -1.201621 -1.605522 -1.004900 2
5 -0.295025 1.073173 -0.269690 -0.335601 0.004645 -0.558817 -1.394283 -0.408601 -0.492399 0.109844 -0.502031 -1.097589 -1.789211 0.112535 3
6 0.579801 1.073173 1.273700 2.432271 1.262586 1.385956 -1.394283 0.517484 2.915704 1.356169 2.789945 -1.005031 -1.467755 -0.767759 1
7 -1.794725 1.073173 -1.813079 0.356367 -1.127502 -1.207075 -0.319423 -0.408601 2.308510 0.292442 2.280113 -0.925477 -1.238143 -0.832434 1
8 -1.169850 1.073173 0.502005 0.356367 0.004645 0.737698 -0.319423 -2.260770 1.556752 1.666218 1.403786 -0.997382 -1.789211 -0.598886 1
9 0.204875 1.073173 -0.269690 1.048335 0.004645 1.385956 0.755438 0.517484 0.361805 0.636805 0.303521 -0.962577 -1.421833 -0.487502 1
In [ ]:
# dt_estandarizado.to_csv('dt_estandarizado.csv', index=False)
In [ ]:
clusters_df = pd.read_csv('Clusters.csv')
In [ ]:
cluster_profiles = clusters_df.groupby('cluster').mean()
cluster_profiles
Out[ ]:
Customer_Age Gender Dependent_count Income_Category Months_on_book Total_Relationship_Count Months_Inactive_12_mon Contacts_Count_12_mon Credit_Limit Total_Revolving_Bal Avg_Open_To_Buy Total_Trans_Amt Total_Trans_Ct Avg_Utilization_Ratio
cluster
0 -0.083396 0.275192 0.082837 0.158520 -0.051089 -1.293076 -0.157992 -0.305445 0.619626 0.232077 0.598013 2.529214 1.549494 -0.408194
1 0.054266 1.009903 0.120356 1.151095 0.055428 0.167387 -0.001292 0.117423 1.446687 0.025396 1.443272 -0.324195 -0.327768 -0.751375
2 -0.032875 -0.302539 -0.027219 -0.415644 -0.018287 0.047025 0.086753 0.116980 -0.339665 -0.996148 -0.248664 -0.249258 -0.193404 -0.787522
3 0.014969 -0.298013 -0.051565 -0.259399 -0.002163 0.145416 -0.029356 -0.075302 -0.537104 0.643268 -0.595293 -0.175299 -0.020384 0.973656

Aplicamos la perfilación de los clientes que en este caso fue de 4 grupos, y como podemos observar en nuestro dataframe se ha agregado una nueva columna la cual nos indica el numero de grupos al que pertenece cada cliente.

Para realizar el perfilado de los 4 nuevos clústeres, examinaremos las estadísticas descriptivas proporcionadas para cada uno, enfocándonos en los valores medios para obtener una idea general de las características distintivas de los clientes en cada clúster.

Cluster 0: Clientes Activos y Cautelosos

  • Edad promedio ligeramente por debajo de la media del conjunto de datos.
  • Ligeramente más hombres que mujeres.
  • Un número ligeramente superior de dependientes.
  • Ingresos ligeramente por encima del promedio, indicando una posible tendencia hacia ingresos más altos.
  • Duración promedio de la relación con el banco y un bajo número total de productos.
  • Actividad reciente y contacto con el banco por debajo del promedio.
  • Límites de crédito por encima del promedio y saldos de crédito rotativos ligeramente por encima del promedio.
  • Alta utilización de la línea de crédito disponible.
  • Transacciones totales significativamente por encima del promedio en cantidad y monto.
  • Baja utilización del crédito en comparación con otros clústeres.

Este grupo está caracterizado por clientes relativamente jóvenes, con una distribución de género equilibrada y un número promedio de dependientes. Tienen ingresos moderados y una relación establecida con el banco, aunque utilizan menos productos financieros en comparación con otros clusters. Muestran una alta actividad de transacciones, lo que sugiere un uso activo de los servicios bancarios, y tienen un límite de crédito y saldo de crédito rotativo por encima del promedio. Sin embargo, la utilización del crédito es baja, lo que indica una gestión prudente de sus finanzas.

Cluster 1: Clientes Prósperos y Moderados

  • Edad promedio ligeramente por encima del conjunto de datos.
  • Predominantemente hombres.
  • Número ligeramente superior de dependientes.
  • Altos ingresos en comparación con otros clústeres.
  • Duración de la relación con el banco y número total de productos ligeramente por encima del promedio.
  • Nivel de inactividad y contacto con el banco promedio.
  • Límites de crédito y saldos de crédito rotativos significativamente más altos.
  • Una de las utilizaciones más bajas de la línea de crédito disponible.
  • Actividad de transacciones por debajo del promedio tanto en cantidad como en monto.

Este grupo está compuesto principalmente por hombres de mediana edad con un número ligeramente superior de dependientes. Tienen ingresos significativamente altos y una relación estable con el banco, utilizando una cantidad moderada de productos financieros. Aunque muestran una actividad bancaria promedio, su utilización del crédito es muy baja, a pesar de tener un límite de crédito considerable y un saldo de crédito rotativo moderado. Las transacciones son un poco menos frecuentes y de menor valor en comparación con otros clusters.

Cluster 2: Clientes Conservadores y Moderados

  • Edad promedio cercana a la media del conjunto de datos.
  • Más mujeres que hombres.
  • Dependientes y categoría de ingresos ligeramente por debajo del promedio.
  • Duración promedio de la relación con el banco.
  • Número total de productos y nivel de inactividad promedio.
  • Contacto con el banco y límites de crédito por debajo del promedio.
  • Saldos de crédito rotativos significativamente bajos.
  • Utilización más baja del crédito disponible.
  • Actividad de transacciones ligeramente por debajo del promedio en cantidad y monto.

En este grupo, encontramos clientes de diversas edades, pero en su mayoría mujeres, con ingresos relativamente bajos. Tienen una relación estable con el banco y utilizan una cantidad promedio de productos financieros. Su actividad bancaria es moderada, al igual que su límite de crédito y saldo de crédito rotativo, que son inferiores al promedio. La utilización del crédito es muy baja, lo que sugiere una gestión conservadora de sus recursos financieros.

Cluster 3: Clientes Dependientes y Utilizadores

  • Edad promedio cercana a la media.
  • Más mujeres que hombres.
  • Dependientes y categoría de ingresos ligeramente por debajo del promedio.
  • Duración promedio de la relación con el banco.
  • Número total de productos y nivel de inactividad promedio.
  • Menos contacto con el banco y límites de crédito por debajo del promedio.
  • Saldos de crédito rotativos más altos que el promedio.
  • La utilización más alta del crédito disponible en comparación con otros clústeres.
  • Actividad de transacciones cercana al promedio tanto en cantidad como en monto.

Este grupo incluye clientes de diferentes edades, mayoritariamente mujeres, con ingresos moderados. Tienen una relación sólida con el banco y utilizan una cantidad ligeramente superior de productos financieros en comparación con otros clusters. Aunque muestran una actividad bancaria algo más baja que la media, tienen un límite de crédito y saldo de crédito rotativo relativamente bajos. Sin embargo, su alta utilización del crédito indica una dependencia significativa de los recursos bancarios para cubrir sus necesidades financieras.

5. Graficando los resultados¶

Aplicando PCA¶

Realizamos la reducción de dimenciones, con 2 componentes aplicando la tecnica de PCA, posteriormente lo convertimos en un dataframe para poder visualizarlo de una manera más clara.

In [ ]:
# Aplicar PCA
pca = PCA(n_components=2)  # Reducir a 2 componentes principales
data_pca = pca.fit_transform(dt_estandarizado)
In [ ]:
# Aplicar K-Means con el número óptimo de clusters
kmeans = KMeans(n_clusters=4, random_state=0)
clusters = kmeans.fit_predict(data_pca)
In [ ]:
# Crear un DataFrame para visualización
df_visual = pd.DataFrame(data_pca, columns=['PCA1', 'PCA2'])
df_visual['Cluster'] = clusters

Gráfica de dispersión¶

In [ ]:
# Gráfico de dispersión de los clusters
plt.figure(figsize=(10, 10))
sns.scatterplot(x='PCA1', y='PCA2', hue='Cluster',
                data=df_visual, palette='viridis')
plt.title('Clusters de Clientes en Componentes Principales')
plt.show()
No description has been provided for this image

En la siguiente Gráfica podemos observar los 2 componente que hemos reducido y podemos ver a los grupos que pertenecen, los grupos estan representados por diferentes colores.

6. Conclusiones¶

Este trabajo proporciona una visión profunda del comportamiento financiero de los clientes de un banco, utilizando técnicas de segmentación como el análisis de clústeres. A través del análisis de datos y la aplicación de algoritmos de aprendizaje no supervisado, se han identificado diferentes grupos de clientes con características financieras y comportamientos distintivos. Esto permite a la institución financiera comprender mejor a su base de clientes y adaptar estrategias específicas para satisfacer las necesidades de cada segmento.

Las conclusiones específicas de cada clúster proporcionan información valiosa sobre las preferencias, hábitos de consumo y perfiles de riesgo de los clientes, lo que puede guiar decisiones empresariales relacionadas con la oferta de productos, servicios y campañas de marketing personalizadas.

Este trabajo demuestra el valor de la segmentación de clientes en la industria bancaria para mejorar la satisfacción del cliente, la retención y lealtad, y, en última instancia, impulsar el crecimiento y la rentabilidad de la institución financiera.

Los pasos clave incluyeron la exploración y preparación de datos, la segmentación utilizando k-means y la interpretación de los resultados para perfilar distintos grupos de clientes.

  1. Preparación y Exploración de Datos: Se realizó una cuidadosa preparación y exploración de los datos, lo que incluyó la estandarización de variables, tratamiento de valores atípicos y nulos, y la transformación de variables categóricas. Este paso fue crucial para garantizar la calidad y la utilidad de los datos para el análisis posterior.

  2. Segmentación de Clientes: Utilizando el algoritmo k-means, se identificaron 4 clusters distintos dentro de la base de clientes. La técnica del codo se aplicó para determinar el número óptimo de clusters, asegurando una segmentación efectiva y significativa.

  3. Perfilado de Clientes: Cada cluster reveló patrones únicos en términos de edad, ingresos, comportamiento de crédito y uso de productos bancarios. Estos perfiles proporcionan insights valiosos sobre las necesidades y preferencias de diferentes segmentos de clientes.

Esta práctica demuestra el poder de la minería de datos y el análisis de segmentación en la comprensión profunda de una base de clientes. Las estrategias basadas en datos pueden llevar a una mejor toma de decisiones y a un enfoque más centrado en el cliente en el sector bancario.

El análisis de segmentación de clientes en el ámbito bancario demostró ser una herramienta poderosa para entender mejor la base de clientes. A través de una meticulosa preparación y exploración de datos, seguida de la segmentación utilizando el algoritmo k-means, se identificaron perfiles de cliente distintivos. Estos perfiles proporcionan una base sólida para estrategias personalizadas de marketing y desarrollo de productos, y ofrecen insights valiosos para la toma de decisiones y la gestión de riesgos. La práctica destacó la importancia de un enfoque analítico y basado en datos en el sector financiero, subrayando el potencial de estos métodos para mejorar la comprensión y el servicio al cliente en un mercado competitivo.